#pragma once
#include <iostream> 
#include <string>
#include <fstream>
#include <cstdarg>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctime>
#include <unistd.h>
#include <mutex>
#include <condition_variable>
#include <map>
#include <semaphore.h>
class Sem{
    sem_t _sem;

    public:
    Sem( int Soruce_InitNums =0,int process_share = 0)
    {
        sem_init(&_sem,process_share,Soruce_InitNums);
    }
    //放入资源
    void Push()
    {
        sem_post(&_sem);
    }
    //拿走资源
    void Get()
    {
        sem_wait(&_sem);
    }
    ~Sem()
    {
        sem_destroy(&_sem);
    }
    //禁用复制重载和拷贝
    Sem(Sem&) = delete;
    Sem& operator=(Sem&) = delete;
};

std::map<std::string, std::string> parseConfigFile(const std::string& filePath) {  
    std::map<std::string, std::string> configMap;  
    std::ifstream configFile(filePath);  
    if (!configFile.is_open()) {  
        std::cerr << "无法打开配置文件: " << filePath << std::endl;  
        return configMap;  
    }  
  
    std::string line;  
    while (std::getline(configFile, line)) {  
        // 去除行尾的注释（如果有的话）  
        size_t commentPos = line.find('#');  
        if (commentPos != std::string::npos) {  
            line = line.substr(0, commentPos);  
        }  
  
        // 忽略空行  
        if (line.empty()) continue;  
  
        // 找到'='的位置  
        size_t pos = line.find('=');  
        if (pos == std::string::npos) {  
            // 如果没有找到'='，则忽略该行  
            continue;  
        }  
  
        // 提取键和值，并去除前后的空格  
        std::string key = line.substr(0, pos);  
        std::string value = line.substr(pos + 1);  
        key.erase(0, key.find_first_not_of(" \t"));  
        key.erase(key.find_last_not_of(" \t") + 1); 
        value.erase(0, value.find_first_not_of(" \t"));  
        value.erase(value.find_last_not_of(" \t\r\n") + 1);  
  
        // 将键值对添加到map中  
        configMap[key] = value;  
    }  
  
    configFile.close();  
    return configMap;  
}  ;

class Log{
    
    

    std::string LevelToString(int level)
    {
        std::string strLevel;

        switch (level)
        {
            case MsgLevel::Debug:
                strLevel = "Debug";
                break;
            case MsgLevel::Info:
                strLevel = "Info";
                break;
            case MsgLevel::Warning: 
                strLevel = "Warning";
                break;
            case MsgLevel::Error:
                strLevel = "Error";
                break;
            case MsgLevel::Fatal:
                strLevel = "Fatal";
                break;
            default:
                // 如果 level 不属于上述任何一个枚举值，可以提供一个默认值或者抛出异常等
                strLevel = "Unknown";
                break;
        }

        return strLevel;
    }
    public:
        enum MsgLevel
        {
            Debug = 8,
            Info = 9,
            Warning = 10,
            Error  =11,
            Fatal = 12
        };
        enum PrintTo{
            LogNormol   = 8,
            Screen = 0,
            LogErorr =11,
            LogFatal = 12,
            All,
        };
        Log(int file_style = PrintTo ::Screen,const std::string& filename = "");
        ~Log();

    std:: string GetNowTime()
    {
        //timedatectl   # 查询时区
        //sudo timedatectl set-timezone Asia/Shanghai  # 修改时区
        // printf("[Level:%s] [%s]  {%s}\n", LevelToString(level).c_str(), buffer,std::to_string(currtime).c_str());
        time_t CurTim;
        time(&CurTim);
        struct tm *currtime = localtime(&CurTim);//注意，这里获取的时系统中的Localtime，由于linux大多数默认时区是美国那边的，故而会有时差
        char buffer[128];
        snprintf(buffer,sizeof(buffer),"time::%d:%d  <> %d-%d-%d",currtime->tm_hour,currtime->tm_min,currtime->tm_mday,currtime->tm_mon+1,currtime->tm_year+1900);
        return buffer;
    }
    void operator()(int level, const char *format, ...){
        char buffer[1024];
        va_list args;
        va_start(args, format);
        //使得args指向可变参数部分

        vsnprintf(buffer, sizeof(buffer), format, args);
        va_end(args);
        
        char buffer2[2048];

        //根据日志等级分级
        if(_IsUseMsgLevel)
        {
            Style(level);
        }
        snprintf(buffer2,sizeof(buffer2),"[Level:%s][PID:%d] [%s]  {%s}\n", LevelToString(level).c_str(), getpid(),buffer,GetNowTime().c_str());
        WriteLog(buffer2);
    }
    void WriteToFile(const std::string &filename,const std::string & Msg)
    {
        std::ofstream ofs(_filename + filename,std::ios::app | std::ios::ate);
        if(!ofs.is_open())
        {
            return ;
        }
        {
            _static_Sem.Get();
            ofs<<Msg;
            ofs.flush();
            _static_Sem.Push();
        }
        ofs.close();

    }
    void WriteLog(const std::string& Msg)
    {
        switch (_file_style)
        {
        case PrintTo::Screen:
            {
                _static_Sem.Get();
                std::cout<<Msg;
                _static_Sem.Push();

            }
            break;


        //可以在此处修改，让日志名与等级挂钩
        case MsgLevel::Debug:
        case MsgLevel::Info:
        case MsgLevel::Warning:
                WriteToFile("NormolLog.txt",Msg);
            break;
        case MsgLevel::Fatal:
            WriteToFile("FatalLog.txt",Msg);
        break;
        case MsgLevel::Error:
                WriteToFile("ErrorLog.txt",Msg);
            break;
        default:
            WriteToFile("OtherLog.txt",Msg);
        }
    }
    void Style(int Style = PrintTo::Screen)
    {
        _file_style = Style;
    }

    private:
    int _file_style = PrintTo::Screen;
    std::string _filename;
    int _IsUseMsgLevel = 0;
    //其实每一种日志文件对应一个信号量，效率理应才最搞，这里就不多实现
    static Sem _static_Sem;
};

//打印语言的缓冲区一般都只有一个，所以我们就设置只有一个初始值即可
Sem Log::_static_Sem(1);

Log::Log(int file_style,const std::string& filename)
:_file_style(file_style)
{
    mkdir("Log",0777);
    if(parseConfigFile("Log.conf")["IsUseMsgLevel"]=="true")
    {
        _IsUseMsgLevel = true;
        _filename = "./Log/"+filename;

    }
    
}

Log::~Log()
{

}


//封装进命名空间，可以直接调用
namespace myService{
    Log lg;
}